/* Encode & Decode 
 * Copyright(C) 2002 - 2005, ME2Soft. All rights reserved!
 * Author: Leon Cheng
 * $Id: ME2MailEsc.cpp,v 1.1 2008/10/17 06:08:18 leon Exp $
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ME2MailEsc.h"

static const char uu_std[65] =
{
  '`', '!', '"', '#', '$', '%', '&', '\'',
  '(', ')', '*', '+', ',', '-', '.', '/',
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', ':', ';', '<', '=', '>', '?',
  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  'X', 'Y', 'Z', '[', '\\', ']', '^', '_',0
};
static const char uu_base64[65] =
{
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  'w', 'x', 'y', 'z', '0', '1', '2', '3',
  '4', '5', '6', '7', '8', '9', '+', '/','='
};
static const char b64_tab[256] =
{
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*000-007*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*010-017*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*020-027*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*030-037*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*040-047*/
	'\177', '\177', '\177', '\76',  '\177', '\177', '\177', '\77',  /*050-057*/
	'\64',  '\65',  '\66',  '\67',  '\70',  '\71',  '\72',  '\73',  /*060-067*/
	'\74',  '\75',  '\177', '\177', '\177', '\100', '\177', '\177', /*070-077*/
	'\177', '\0',   '\1',   '\2',   '\3',   '\4',   '\5',   '\6',   /*100-107*/
	'\7',   '\10',  '\11',  '\12',  '\13',  '\14',  '\15',  '\16',  /*110-117*/
	'\17',  '\20',  '\21',  '\22',  '\23',  '\24',  '\25',  '\26',  /*120-127*/
	'\27',  '\30',  '\31',  '\177', '\177', '\177', '\177', '\177', /*130-137*/
	'\177', '\32',  '\33',  '\34',  '\35',  '\36',  '\37',  '\40',  /*140-147*/
	'\41',  '\42',  '\43',  '\44',  '\45',  '\46',  '\47',  '\50',  /*150-157*/
	'\51',  '\52',  '\53',  '\54',  '\55',  '\56',  '\57',  '\60',  /*160-167*/
	'\61',  '\62',  '\63',  '\177', '\177', '\177', '\177', '\177', /*170-177*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*200-207*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*210-217*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*220-227*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*230-237*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*240-247*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*250-257*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*260-267*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*270-277*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*300-307*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*310-317*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*320-327*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*330-337*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*340-347*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*350-357*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*360-367*/
	'\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*370-377*/
};

int Base64_encode(const unsigned char* lpSrc,int nSrc,unsigned char* lpDest,int *lpnSize, int bAddLF/*0*/)
{
	if ( !lpSrc || !lpDest || !nSrc || !lpnSize) return 0;
	int	nSite=0;
	int	nDest=0;
	int nLine = 0;
	char	s1,s2,s3,d1,d2,d3,d4;
	while( nSite < nSrc ) {
		s1	=	*(lpSrc+nSite++);
		d1	=	(s1>>2) & 0x3f;
		d2	=	(s1<<4) & 0x30;
		if ( nSite < nSrc ) {
			s2	=	*(lpSrc+nSite++);
			d2	+=	(s2>>4) & 0x0f;
			d3	=	(s2<<2) & 0x3c;
			if ( nSite < nSrc ) {
				s3	=	*(lpSrc+nSite++);
				d3	+=	(s3>>6) & 0x03;
				d4	=	s3 & 0x3f;
			} else {	//only s1,s2
				d4	= 64;
			}
		} else {		//only s1
			d3	= 64;
			d4	= 64;
		}
		if ( nDest+4 > *lpnSize ) return 0;
		*(lpDest + nDest++)	=uu_base64[d1];
		*(lpDest + nDest++)	=uu_base64[d2];
		*(lpDest + nDest++)	=uu_base64[d3];
		*(lpDest + nDest++)	=uu_base64[d4];
		if(bAddLF && ++nLine % 20 == 0 && nDest < *lpnSize) 
			*(lpDest + nDest++) = '\n';
	}
	if ( d4 != 64 ) {
		if ( nDest+2 > *lpnSize ) return 0;
		*(lpDest + nDest++)	=uu_base64[64];
	}
	*(lpDest + nDest)		=0;
	*lpnSize=nDest;
	return 1;
}

int Base64_decode(const unsigned char* lpSrc,int nSrc,unsigned char* lpDest,int *lpnSize)
{
	int nDest;
	char cc,num;
	if( !lpnSize || !lpDest || !lpSrc ){
		return 0;
	}
	const unsigned char* lpSrcEnd = lpSrc + nSrc;
	for( num=nDest=0; *lpSrc && lpSrc < lpSrcEnd; lpSrc++)
	{
		if( nDest >= *lpnSize ){
			return 0;
		}
		if( (cc=b64_tab[*lpSrc])==64 ) {
			break;
		}
		if( cc>64 ){
			continue;
		}
		switch( num ){
		case 0:
			*(lpDest+nDest)    = (cc<<2);
			num++;
			break;
		case 1:
			*(lpDest+nDest++) += (cc>>4) & 0x03;
			*(lpDest+nDest)    = (cc<<4);
			num++;
			break;
		case 2:
			*(lpDest+nDest++) += (cc>>2) & 0x0f;
			*(lpDest+nDest)    = (cc<<6) & 0xc0;
			num++;
			break;
		case 3:
			*(lpDest+nDest++) += cc & 0x3f;
			num=0;
			break;
		}
	}
	*(lpDest+nDest)=0;
	*lpnSize=nDest;
	return 1;
}

int UU_encode(const unsigned char* lpSrc,int nSrc,unsigned char* lpDest,int *lpnSize)
{
	if ( !lpSrc || !lpDest || !nSrc || !lpnSize) return 0;
	int	nSite=0;
	int	nDest=0;
	char	s1,s2,s3,d1,d2,d3,d4;
	while( nSite < nSrc ) {
		s1	=	*(lpSrc+nSite++);
		d1	=	(s1>>2) & 0x3f;
		d2	=	(s1<<4) & 0x30;
		if ( nSite < nSrc ) {
			s2	=	*(lpSrc+nSite++);
			d2	+=	(s2>>4) & 0x0f;
			d3	=	(s2<<2) & 0x3c;
			if ( nSite < nSrc ) {
				s3	=	*(lpSrc+nSite++);
				d3	+=	(s3>>6) & 0x03;
				d4	=	s3 & 0x3f;
			} else {	//only s1,s2
				d4	= 64;
			}
		} else {		//only s1
			d3	= 64;
			d4	= 64;
		}
		if ( nDest+4 > *lpnSize ) return 0;
		*(lpDest + nDest++)	=uu_std[d1];
		*(lpDest + nDest++)	=uu_std[d2];
		*(lpDest + nDest++)	=uu_std[d3];
		*(lpDest + nDest++)	=uu_std[d4];
	}
	if ( d4 != 64 ) {
		if ( nDest+2 > *lpnSize ) return 0;
		*(lpDest + nDest++)	=uu_base64[64];
	}
	*(lpDest + nDest++)		=0;
	*lpnSize=nDest;
	return 1;
}

int UU_decode(const unsigned char* lpSrc, int nSrc, unsigned char* lpDest,int *lpnSize)
{
	int nDest;
	unsigned char cc,num;
	if( !lpSrc || !lpDest || !lpnSize ){
		return 0;
	}
	const unsigned char* lpSrcEnd = lpSrc + nSrc;
	for( num=nDest=0; *lpSrc && lpSrc < lpSrcEnd; lpSrc++)
	{
		if( nDest >= *lpnSize ){
			return 0;
		}
		cc = *lpSrc;
		if( cc<' ' || cc>96 ){
			continue;
		}
		if( cc==96 ){
			cc=0;
		} else{
			cc-=' ';
		}
		switch( num ){
		case 0:
			*(lpDest+nDest)    = (cc<<2) & 0xfc;
			num++;
			break;
		case 1:
			*(lpDest+nDest++) += (cc>>4) & 0x03;
			*(lpDest+nDest)    = (cc<<4) & 0xf0;
			num++;
			break;
		case 2:
			*(lpDest+nDest++) += (cc>>2) & 0x0f;
			*(lpDest+nDest)    = (cc<<6) & 0xc0;
			num++;
			break;
		case 3:
			*(lpDest+nDest++) += cc & 0x3f;
			num=0;
			break;
		}
	}
	*(lpDest+nDest) = 0;
	*lpnSize = nDest;
	return 1;
}

int QuotedPrint_encode(const unsigned char* lpSrc,int nSrc,unsigned char* lpDest,int *lpnSize, int nAddLF/*=0*/)
{
	if ( !lpSrc || !lpDest || !nSrc || !lpnSize) return 0;
	int	nSite=0;
	int	nDest=0;
	char	cc;
	while( nSite < nSrc ) {
		cc	=	*(lpSrc+nSite++);
		if (' ' <= cc && cc <= '<' || '>' <= cc && cc <= '~') {
			*(lpDest+nDest++)	=	cc;
		} else if(cc == '\r' || cc == '\n') {
			*(lpDest+nDest++)	=	cc;
		} else {
			sprintf((char*)lpDest+nDest++,"=%02X", (unsigned char)cc);
			nDest += 2;
		}
	}
	*(lpDest+nDest++)	=	0;
	*lpnSize	=	nDest;
	return 1;
}

#define IsHexAscii(c) (((c)>='0' && (c)<='9')||((c)>='A' && (c)<='F')||((c)>='a' && (c)<='f'))

int QuotedPrint_decode(const unsigned char* lpSrc, int nSrc, unsigned char* lpDest,int *lpnSize)
{
	int	nDest;
	if( !lpSrc || !lpDest || !lpnSize){
		return 0;
	}
	const unsigned char* lpSrcEnd = lpSrc + nSrc;
	for( nDest=0; *lpSrc && lpSrc < lpSrcEnd; lpSrc++){
		if( nDest >= *lpnSize ){
			return 0;
		}
		if( lpSrc[0] == '=' ){
			if( IsHexAscii(lpSrc[1]) && IsHexAscii(lpSrc[2]) ){
				char code[4] = { lpSrc[1], lpSrc[2], 0, 0 };
				lpDest[nDest++] = (char)strtoul(code,0,16);
				lpSrc += 2;
			} else if( lpSrc[1]=='\r' && lpSrc[2]=='\n' ){
				lpSrc += 2;
			} else if( lpSrc[1]=='\n' ){
				lpSrc ++;
			}
		} else {
			lpDest[nDest++] = lpSrc[0];
		}
	}
	lpDest[nDest]=0;
	*lpnSize=nDest;
	return 1;
}

unsigned char* URL_decode(unsigned char* lpszDest)
{
	unsigned char* lpsz=lpszDest;
	while( *lpszDest++ ) {
		if ( *lpszDest == '+' ) {
			*lpszDest = ' ';
		}
	}
	lpszDest=lpsz;
	while( (lpszDest=(unsigned char*)strchr((const char*)lpszDest,'%')) ) {
		char szBuf[4]={lpszDest[1],lpszDest[2],0,0};
		lpszDest[0] = (char)strtoul(szBuf,0,16);
		memcpy(lpszDest+1,lpszDest+3,strlen((const char*)lpszDest)-2);
		lpszDest++;
	}
	return lpsz;
}

const char* FindLP1(const char* lp0)
{
	while( (lp0=strchr(lp0,'?')) ){
		lp0--;
		if(*lp0=='Q'||*lp0=='q'||*lp0=='B'||*lp0=='b'){
			return lp0;
		}
		lp0+=2;
	}
	return 0;
}

char* RFC822_decode(unsigned char* lpSrc,char* lpDest, CLStr* charset)
{
	int i;
	unsigned char* lp0,*lp1,*lp2;
	char * cTemp = lpDest;
	unsigned char* lp11,*lp22;
	while( (lp0=(unsigned char*)strstr((const char*)lpSrc,"=?")) &&
		   (lp1=(unsigned char*)FindLP1((const char*)lp0+2)) &&
	 	   (lp2=(unsigned char*)strstr((const char*)lp1+2,"?=")) )
	{
		if(charset) charset->assign((const char*)lp0+2, lp1-lp0-3);
		lp11=lp1;
		lp22=lp2;
		if( (*lp1=='Q' || *lp1=='q') && IsHexAscii(lp2[2]) && IsHexAscii(lp2[3]) ){
			char* lp=strstr((const char*)lp2+2,"?=");
			if( lp ){
				lp2=(unsigned char*)lp;
			}
		}
		*(short*)lp2=0;
		*(short*)lp0=0;
		lp0+=2;
		int len=lp1-lp0-1;
		while(*lpSrc>=' '){*lpDest++ = *lpSrc++;}
		if( *lp1=='B' || *lp1=='b' ){
			i=0x76543210;
			if( Base64_decode(lp1+2, strlen((const char*)lp1+2), (unsigned char*)lpDest,&i) ){
				lpDest+=i;
			}
		} else {// if( *lp1=='Q' || *lp1=='q' ){
			i=0x76543210;
			if( QuotedPrint_decode(lp1+2, strlen((const char*)lp1+2), (unsigned char*)lpDest,&i) ){
				lpDest+=i;
			}
		}
		lpSrc=lp2+2;
	}
	strcpy(lpDest,(const char*)lpSrc);
	return cTemp;
}
/* HZ_decode 
 * ~{abcd22~}
 *
 *
 */
char* HZ_decode(char* lpSrc)
{
	if(!lpSrc) return NULL;
	char* lpSrcEnd = lpSrc + strlen(lpSrc);
	char* lpDest = lpSrc;
	char* lpHzEnd = lpSrc;
	char* lpHzBegin;
	int i;
	while((lpHzBegin = strstr(lpHzEnd, "~{"))) {
		memcpy(lpDest, lpHzEnd, lpHzBegin - lpHzEnd);
		lpDest += lpHzBegin - lpHzEnd;
		
		if(!(lpHzEnd = strstr(lpHzBegin + 2, "~}"))) lpHzEnd = lpSrcEnd;
		memcpy(lpDest, lpHzBegin + 2, lpHzEnd - lpHzBegin - 2);
		for(i = 0; i < lpHzEnd - lpHzBegin - 2; i++) {
			*lpDest |= 0x80;
			lpDest++;
		}			
		if(lpHzEnd == lpSrcEnd) break;
		else lpHzEnd += 2;
	}
	if(lpHzEnd < lpSrcEnd && lpDest != lpSrc) memcpy(lpDest, lpHzEnd, lpSrcEnd - lpHzEnd);
	return lpSrc;	
}      
